/**
 * Copyright 2018-2020 stylefeng & fengshuonan (https://gitee.com/stylefeng)
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.ibdsr.human.interceptor;


import cn.ibdsr.human.constant.ErrorTip;
import cn.ibdsr.human.constant.PlatformEnum;
import cn.ibdsr.human.exception.BizExceptionEnum;
import cn.ibdsr.human.util.*;
import cn.ibdsr.human.constant.Const;
import cn.ibdsr.human.constant.JwtConstants;

import org.apache.shiro.util.ThreadContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.DependsOn;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;


/**
 * @Description Rest Api接口鉴权
 * @Version V1.0
 * @CreateDate 2019/5/7 13:34
 * <p>
 * Date           Author               Description
 * ------------------------------------------------------
 * 2019/5/7      Wujiayun            类说明
 */
@Component
@DependsOn("springContextHolder")
public class RestApiInteceptor extends HandlerInterceptorAdapter {

    private final static Logger logger = LoggerFactory.getLogger(RestApiInteceptor.class);

    private RedisUtil redisUtil = SpringContextHolder.getBean(RedisUtil.class);


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        if (handler instanceof ResourceHttpRequestHandler) {
            return true;
        }
        Boolean flag = check(request, response);
        if (flag) {
            request.setAttribute(Const.USER_ID, ThreadContext.get(Const.USER_ID));
        }
        return flag;
    }

    private boolean check(HttpServletRequest request, HttpServletResponse response) throws IOException {

        if (request.getServletPath().indexOf("swagger") != -1) {
            return true;
        }
        if (request.getServletPath().length() == 1) {
            return true;
        }

        if(!StringUtils.isEmpty( request.getServletPath())){
            Integer index = request.getServletPath().indexOf("no-login");
            if (index != -1){
                return  true;
            }
        }
        if (Arrays.asList(JwtConstants.AUTH_PATH).contains(request.getServletPath())) {
            return true;
        }

        String token = null;
        String requestHeader;

        // get请求直接从参数中获取token值
        if (Arrays.asList(JwtConstants.GET_PATH).contains(request.getServletPath())) {
            requestHeader = request.getParameter(JwtConstants.AUTH_HEADER);
        } else {
            requestHeader = request.getHeader(JwtConstants.AUTH_HEADER);
        }

        logger.info("-------------------------------------其他接口requestHeader：{}", requestHeader);

        if (requestHeader != null && requestHeader.startsWith("Bearer ")) {
            token = requestHeader.substring(7);
        }

        if (StringUtils.isEmpty(token)) {

            logger.info("-------------------------------------request.getServletPath()：{}", request.getServletPath());
            response.setStatus(HttpStatus.BAD_REQUEST.value());
            RenderUtil.renderJson(response, new ErrorTip(BizExceptionEnum.TOKEN_ERROR.getCode(), BizExceptionEnum.TOKEN_ERROR.getMessage()));
            return false;
        }

        // 获取存在Token中的数据，存在user中
        String userInfoStr = JwtTokenUtil.getUserIdFromToken(token);
        if (StringUtils.isEmpty(userInfoStr)) {
            // token解析失败
            response.setStatus(HttpStatus.BAD_REQUEST.value());
            RenderUtil.renderJson(response, new ErrorTip(BizExceptionEnum.TOKEN_ERROR.getCode(), BizExceptionEnum.TOKEN_ERROR.getMessage()));
            return false;
        }

        String[] userInfos = userInfoStr.split(":");
        // 用户ID
        Integer sysType = Integer.valueOf(userInfos[0]);
        Long userId = Long.valueOf(userInfos[1]);
        // 账号
        String account = userInfos[2];
        // redis中的key
        String redisKey = JwtConstants.SCENICBOOT +String.valueOf(sysType)+":"+account;

        if (sysType.equals(PlatformEnum.PLATFORM.getCode())) {
            String ip = IPUtils.getIPAddress(request);
            redisKey = JwtConstants.SCENICBOOT + ip + ":" + account;
        }

        // 退出接口，则将Token从redis中删除
        if (Arrays.asList(JwtConstants.LOGOUT_PATH).contains(request.getServletPath())) {
            redisUtil.del(redisKey);
            return true;
        }

        // 从redis中获取token
        Object jwtAuthToken = redisUtil.get(redisKey);
        if (null == jwtAuthToken) {
            // 未在redis找到token，提示过期重新登录
            response.setStatus(HttpStatus.BAD_REQUEST.value());
            RenderUtil.renderJson(response, new ErrorTip(BizExceptionEnum.TOKEN_EXPIRED.getCode(), BizExceptionEnum.TOKEN_EXPIRED.getMessage()));
            return false;
        }

        String authToken = String.valueOf(jwtAuthToken);

        if (!token.equals(authToken)) {
            // 两个token不一样，返回当前账号在另一各设备登录
            logger.info("-----------两个token不一样，返回当前账号在另一各设备登录---------------token：{}; authToken: {}", token, authToken);
            response.setStatus(HttpStatus.BAD_REQUEST.value());
            RenderUtil.renderJson(response,
                    new ErrorTip(BizExceptionEnum.TOKEN_ERROR_OTHER_DEVICE_LOGIN.getCode(), BizExceptionEnum.TOKEN_ERROR_OTHER_DEVICE_LOGIN.getMessage()));
            return false;
        }

        ThreadContext.put(Const.SYS_TYPE, sysType);
        ThreadContext.put(Const.USER_ID, userId);
        ThreadContext.put(Const.USER_ACCOUNT, account);
        return true;
    }

}
